<template>
  <!-- 集成表格组件Vue3.0  v-bind="$attrs"-->
  <el-table
    ref="multipleTable"
    :data="data.tableData"
    :height="props.height"
    :max-height="props.height"
    :span-method="objectSpanMethod"
    :size="size"
    :border="true"
    :stripe="true"
    style="width: 100%"
    @select="handleSelect"
    @select-all="handleSelectAll"
    @header-dragend="changeTableDrag"
  >
    <!-- 多选框列 -->
    <el-table-column v-if="props.select" type="selection" header-align="center" width="65" min-width="60" align="center"></el-table-column>
    <!-- 序号列 -->
    <el-table-column
      v-if="props.serial" type="index" fixed="left" width="100" :index="indexMethod" header-align="center"
      align="center" min-width="60">
      <template #header>
        <div class="d-flex ai-center jc-center">
          <span style="white-space: nowrap;">{{serialText}}</span>
          <!-- <i v-if="serialSelect" class="el-icon-s-tools indexIcon" @click="dragEdit"></i> -->
        </div>
      </template>
    </el-table-column>
    <!-- 内容展示列 -->
    <el-table-column
      v-for="(item, i) in data.cols"
      :key="'array' + i"
      :prop="item.prop"
      :label="item.label"
      :min-width="Number(item.width) || 120"
      :fixed="item.fixed"
      header-align="center"
      show-overflow-tooltip
      :align="item.align || 'center'"
    >
      <template #default="scope">
        <!-- 多级表头 -->
        <template v-if="item.type == 'merge'">
          <columnItem v-for="(item, index) in item.merge" :key="'merge' + index" :col="item">
            <template #custom="val">
              <slot name="customChild" :data="val.data" :column="val.column"></slot>
            </template>
          </columnItem>
        </template>
        <!-- 普通文字的展示 -->
        <template v-if="item.type == 'text'">
          <span @click="item.fn && item.fn(scope.row, scope)">{{ scope.row[item.prop] }}</span>
        </template>
        <!-- 当为输入框的时候 -->
        <template v-if="item.type == 'input'">
          <el-input
            v-if="item.filter ? item.filter(scope.row, scope.row[item.prop]) : true"
            v-model="scope.row[item.prop]"
            :clearable="true"
            @blur="item.fn && item.fn(scope.row[item.prop], scope.$index, item.prop)"
          ></el-input>
          <template v-else>{{ scope.row[item.prop] }}</template>
        </template>
        <!-- 当为下拉选择框的时候 -->
        <template v-if="item.type == 'select'">
          <el-select 
            v-if="item.filter ? item.filter(scope.row, scope.row[item.prop]) : true"
            @change="item.fn && item.fn(scope.row[item.prop], scope.$index, item.prop)"
            :collapse-tags="item.multiple ? true : false"
            v-model="scope.row[item.prop]"
            :placeholder="item.placeholder || '请选择'"
            :clearable="true"
          >
            <el-option
              v-for="(items, i) in item.select"
              :key="'select_' + i"
              :label="items.label || items.text"
              :value="items.value" 
            ></el-option>
          </el-select>
        </template>
        <!-- 自定义 -->
        <template v-if="item.type == 'custom'">
          <slot :name="item.prop" :data="scope.row" :column="item" />
        </template>  
      </template>
    </el-table-column>
    <!-- 操作列 -->
    <el-table-column
      label="操作"
      :min-width="Number(props.changeAlignWidth)"
      :fixed="changeAlign"
      v-if="changeEdit"
      header-align="center"
      align="center"
    >
      <template #default="scope">
        <div class="d-flex ai-center jc-center">
          <slot name="btns" :data="scope.row" :item="scope"></slot>
        </div>
      </template>
    </el-table-column>
  </el-table>

  <!-- 分页组件的位置 -->
  <pagination
    v-if="count"
    :count="parseInt(String(data.count))"
    :fetch="fetch"
    v-model:offset="data.offset"
    v-model:sizes="data.size"
    :limit="limit"
  ></pagination>
</template>

<script lang="ts" setup>

type align = 'left' | 'right' | 'center'

type typeList = 'text' | 'merge' | 'custom' | 'input' | 'select'

// 表格配置项的接口类型
interface cols{
  label: string,
  prop: string,
  type: typeList,
  width?: number | string,
  fixed?: align,
  align?: align,
  merge?: cols[],
  fn?: Function,
  filter?: Function,
  multiple?: boolean,
  placeholder?: string,
  select?: selectList[]
}

// 表格内下拉选择框的参数
interface selectList{
  label?: string,
  value?: string,
  text?: string
}

// 表格分页的参数
interface reqPar{
  pageSize: number,
  pageOffset: number
}

// 表格组件内定义参数的类型
interface data{
  tableData: any[] | String,
  cols: cols[],
  reqPar: reqPar,
  count: number,
  size: number,
  offset: number,
  selDefault: string[]
}

// treeProps
interface treeProps{
  children?: string,
  hasChildren?: string
}

import { defineProps, reactive, defineEmit, watch, ref, useContext, nextTick } from 'vue';
import type { PropType } from "vue";
import columnItem from "./columnItem.vue";
import pagination from "./pagination.vue";

const { expose } = useContext()

const emit = defineEmit([ 'select', 'getTableCheckIds'])

const multipleTable:any = ref(null)

const props = defineProps({
  // 表格高度设置
  height: { type: [ Number, String ], default: 'auto' },
  // 表格数据设置
  tableData:{ type: [ Array, String ],default: ()=>{ return [] } },
  // 表格生成配置项
  cols: { type: Array as PropType<cols[]>,default: ()=>{ return [] } },
  // 表格分页数据
  reqPar:{ type: Object as PropType<reqPar>, default: ()=>{ return { pageSize: 10, pageOffset: 1 } } },
  // 表格分页总条数
  count: { type: Number, default: 0 },
  // 表格初始化请求及其分页查询
  fetch: { type: Function, default: ()=>{} },
  // 是否出现勾选框
  select:{ type: Boolean, default: false },
  // 是否隐藏序号
  serial:{ type: Boolean, default: true },
  // 序号列展示的文字
  serialText:{ type: String, default: '序号' },
  // 修改表格操作列的宽度
  changeAlignWidth: { type: [ Number, String ], default: 120 },
  // 修改表格操作列的位置
  changeAlign:{ type: String, default:'right' },
  // 隐藏表格操作列
  changeEdit:{ type: Boolean, defalut: true },
  // 分页展示页码
  limit:{ type: Array as PropType<number[]>, default(){return [10, 15, 20, 30, 50]}},
  // 开启懒加载
  lazy:{ type: Boolean, default: false },
  // 表示合并行列的字段
  merge:{  type: Array as PropType<string[]>, default(){ return [] }},
  // 表示树表的层级结构,格式为{children: 'children', hasChildren: 'hasChildren'}
  treeProps:{  type: Object as PropType<treeProps>, default(){ return {}}},
  // 表示树表的唯一标识符
  rowKey:{  type: String, default: "" },
  // 默认选中的内容数组
  selDefault:{  type: Array as PropType<string[]>, default(){ return []}},
  // 设置为单选
  onlyOne:{  type: Boolean, default: false},
  // 加载动画
  loading:{  type: Boolean, default: false },
  // table表格的尺寸 
  size:{ type: String, default: 'small'},
})

// 存放数据
let data: data = reactive({
  tableData: props.tableData,
  cols: props.cols,
  reqPar: props.reqPar,
  count: props.count,
  size: 10,
  offset: 1,
  selDefault: props.selDefault
})

// 监听区域
watch(()=> props.tableData,(n: any)=>{
  data.tableData = n
},{
  deep: true,
  immediate:true
})

watch(()=> props.cols,(n: cols[])=>{
  data.cols = n
},{
  deep: true,
  immediate:true
})

watch(()=> props.reqPar,(n:reqPar)=>{
  data.reqPar = n
},{
  deep: true,
  immediate:true
})

watch(()=> props.selDefault,(n:string[])=>{
  data.selDefault = n;
  n.length > 0 && findDefault();
},{
  deep: true,
  immediate:true
})

// 执行函数区域
// 格式化表格序号列的参数
const indexMethod = (index: number)=> {
    // 排序的计算属性
  if (data.count) {
    const numCount = Math.ceil(data.count / data.size);
    let num = 0;
    if (numCount < data.offset) {
      num = (data.offset - 1 - (data.offset - numCount)) * data.size + index + 1;
      data.offset -= 1;
    } else {
      num = (data.offset - 1) * data.size + index + 1;
    }
    return num;
  }
  return index + 1;
}

// 表头拖拽时更新表格
const changeTableDrag = () => {
  nextTick(() => {
    multipleTable.value.doLayout();
  });
}

// 处理勾选逻辑，因为涉及到表格回显勾选以及懒加载的问题，所以需要二次处理一下
const handleSelect = (selection: any[], row: { hasChildren: any; id: string | number; }) => {
    // 树表懒加载 勾选当前项要选择下属懒加载的内容(已实现)
  if (row.hasChildren) {
    const { states } = multipleTable.value.store;
    const children = states.lazyTreeNodeMap[row.id];
    const select = states.selection;
    if (selection.indexOf(row) >= 0) {
      for (const item in children) {
        if (select.indexOf(children[item]) == -1) {
          select.push(children[item]);
        }
      }
      emit("select", select, row);
      // 返回被勾选的id集合
      const selectIds = select.map((v: { id: any }) => v.id);
      emit("getTableCheckIds", selectIds, row.id);
    } else {
      for (const item in children) {
        const key = select.indexOf(children[item]);
        if (key >= 0) {
          select.splice(key, 1);
        }
      }
      emit("select", select, row);
      // 返回被勾选的id集合
      const selectIds = select.map((v: { id: any }) => v.id);
      emit("getTableCheckIds", selectIds, row.id);
    }
  } else {
    emit("select", selection, row);
    // 返回被勾选的id集合
    const selectIds = selection.map((v: { id: any }) => v.id);
    emit("getTableCheckIds", selectIds, row.id);
  }
}

// 全选时勾选拿到下属表的内容(已实现)
const handleSelectAll = (selection: any)=> {
  const children = multipleTable.value.store.states.lazyTreeNodeMap;
  const select = multipleTable.value.store.states.selection;
  const isAllselect = multipleTable.value.store.states.isAllSelected;
  if (isAllselect) {
    for (const item in children) {
      console.log(item);
      children[item].forEach((v: any) => {
        if (select.indexOf(v) == -1) {
          select.push(v);
        }
      });
    }
    emit("select", select, []);
    // 返回被勾选的id集合
    const selectIds = select.map((v: { id: any; }) => v.id);
    emit("getTableCheckIds", selectIds, []);
  } else {
    multipleTable.value.store.states.selection = [];
    emit("select", [], []);
    emit("getTableCheckIds", [], []);
  }
  if(data.tableData.length == 0){
    multipleTable.value.clearSelection()
  }
}

// 合并的方法
const objectSpanMethod = (env:any)=> {
  const { row, column, rowIndex, columnIndex } = env;
  if (props.merge && props.merge.length > 0) {
    // merge为需合并的列pro值
    if (props.merge.includes(column.property)) {
      const spanArr = mergeTableRow(props.tableData, column.property);
      const x_row = spanArr[rowIndex];
      const x_col = x_row > 0 ? 1 : 0;
      return {
        rowspan: x_row,
        colspan: x_col,
      };
    }
    return {
      rowspan: 1,
      colspan: 1,
    };
  }
}

// 将数据进行行列合并格式化
const mergeTableRow = (data:any, spanKey:any)=> {
  const spanArr = [];
  let pos = 0;
  for (let i = 0; i < data.length; i++) {
    if (i === 0) {
      spanArr.push(1);
      pos = 0;
    } else {
      // 判断当前元素与上一个元素是否相同
      // eslint-disable-next-line no-lonely-if
      if (data[i][spanKey] === data[i - 1][spanKey]) {
        spanArr[pos] += 1;
        spanArr.push(0);
      } else {
        spanArr.push(1);
        pos = i;
      }
    }
  }
  return spanArr;
}

// 筛选出需要默认勾选的数据
const findDefault = ()=> {
  if (props.selDefault.length > 0) {
    const selectArr:any = [];
    if(Array.isArray(data.tableData)){
      data.tableData.forEach((v:any) => {
        props.selDefault.forEach((j:any) => {
          if (v.id == j) {
            selectArr.push(v);
          }
        });
      });
      selectDefault(selectArr);
    }
  }
}

// 设置默认项
const selectDefault = (rows:any) =>{
  multipleTable.value?.clearSelection();
  if (rows.length > 0) {
    rows.forEach((row:any) => {
      nextTick(() => {
        multipleTable.value?.toggleRowSelection(row, true);
      });
    });
  } else {
    multipleTable.value?.clearSelection();
  }
}


// 向外暴露出去的参数
expose({
  data
})


</script>

<style lang="scss" scoped>
@import "./portrait.scss"
</style>